package org.apache.solr.ocean.util;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.regex.Pattern;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.ExtendedFieldCache;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;

public class Util {
  public static TimeZone UTC = TimeZone.getTimeZone("UTC");
  public final static Pattern splitPattern = Pattern.compile(",| ");
  private static ThreadLocalDateFormat dateFormatThreadLocal = new ThreadLocalDateFormat();
  
  public static void copy(InputStream is, RandomAccessFile ras, byte[] buf) throws IOException {
    int numRead;
    while ((numRead = is.read(buf)) >= 0) {
      ras.write(buf, 0, numRead);
    }
  }
  
  public static File getDirectory(File root, String path) {
    File file = new File(root, path);
    if (!file.exists()) {
      file.mkdirs();
    }
    return file;
  }
  
  public static DateFormat getThreadLocalDateFormat() {
    return dateFormatThreadLocal.get();
  }
  
  public static String formatDate(Date date) {
    DateFormat dateFormat = getThreadLocalDateFormat();
    return dateFormat.format(date);
  }
  
  public static Date parseDate(String string) throws ParseException {
    DateFormat dateFormat = getThreadLocalDateFormat();
    return dateFormat.parse(string);
  }
  
  private static class ThreadLocalDateFormat extends ThreadLocal<DateFormat> {
    DateFormat proto;

    public ThreadLocalDateFormat() {
      super();
      SimpleDateFormat tmp = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
      tmp.setTimeZone(UTC);
      proto = tmp;
    }

    protected DateFormat initialValue() {
      return (DateFormat) proto.clone();
    }
  }

  public static String getIndexed(String field, double value, IndexSchema schema) {
    return getIndexed(field, value, schema);
  }

  public static String getIndexed(String field, String value, IndexSchema schema) {
    return schema.getField(field).getType().toInternal(value);
  }

  public static Long getLongFromIndexed(String field, String value, IndexSchema schema) {
    String readable = schema.getField(field).getType().indexedToReadable(value);
    return new Long(readable);
  }

  public static void setId(Long value, Document document, IndexSchema schema) {
    String string = value.toString();
    setField(Constants.ID, string, document, schema);
  }

  public static void setSchemaVersion(Float value, Document document, IndexSchema schema) {
    String string = value.toString();
    setField(Constants.SCHEMAVERSION, string, document, schema);
  }

  public static void setSnapshotId(Long value, Document document, IndexSchema schema) {
    String string = value.toString();
    setField(Constants.SNAPSHOTID, string, document, schema);
  }

  public static void setField(String fieldName, String value, Document document, IndexSchema schema) {
    SchemaField schemaField = schema.getField(fieldName);
    Field field = schemaField.getType().createField(schemaField, value, 1.0f);
    document.add(field);
  }

  public static long[] getFieldCacheLong(String field, IndexReader indexReader) throws IOException {
    return ExtendedFieldCache.EXT_DEFAULT.getLongs(indexReader, field);
  }

  public static boolean isTrue(String string) {
    return StringUtils.equalsIgnoreCase("true", string);
  }

  public static Object construct(Object parameter, Class clazz) throws Exception {
    Constructor constructor = clazz.getDeclaredConstructor(parameter.getClass());
    try {
      return constructor.newInstance(parameter);
    } catch (InvocationTargetException invocationTargetException) {
      Throwable cause = invocationTargetException.getCause();
      if (cause instanceof Exception)
        throw (Exception) cause;
      else
        throw invocationTargetException;
    }
  }

  public static Long parseLong(String value) {
    if (StringUtils.isBlank(value)) {
      return null;
    }
    try {
      return new Long(value);
    } catch (Throwable ex) {
      return null;
    }
  }

  public static URL parseURL(String string) throws MalformedURLException {
    if (StringUtils.isBlank(string)) {
      return null;
    }
    return new URL(string);
  }

  public static List<String> readLines(Reader reader) throws Exception {
    return IOUtils.readLines(reader);
  }

  public static IOException asIOException(String message, Throwable throwable) throws IOException {
    IOException ioException = new IOException(message);
    ioException.initCause(throwable);
    return ioException;
  }

  public static IOException asIOException(Throwable throwable) {
    if (throwable instanceof IOException) {
      return (IOException) throwable;
    }
    IOException ioException = new IOException(throwable.getMessage());
    ioException.initCause(throwable);
    return ioException;
  }

  public static void copy(InputStream is, OutputStream os, byte[] buf) throws IOException {
    int numRead;
    while ((numRead = is.read(buf)) >= 0) {
      os.write(buf, 0, numRead);
    }
  }

  public static int[] toIntArray(List<Integer> list) {
    int size = list.size();
    int[] array = new int[size];
    int x = 0;
    for (int i : list) {
      array[x] = i;
      x++;
    }
    return array;
  }

  public static List<URL> loadUrls(File file) throws IOException {
    List<String> lines = IOUtils.readLines(new FileReader(file));
    List<URL> urls = new ArrayList<URL>();
    for (String line : lines) {
      urls.add(new URL(line));
    }
    return urls;
  }

  /**
   * public static HttpParameters toHttpParameters(HttpServletRequest request) {
   * try { if (StringUtils.equalsIgnoreCase("post", request.getMethod())) {
   * HttpParameters parameters = new HttpParameters(); URL url = new
   * URL(request.getRequestURL().toString()); CGIParser cgiParser = new
   * CGIParser(url.toString(), "UTF-8"); for (String name :
   * cgiParser.getParameterNameList()) { for (String value :
   * cgiParser.getParameterValues(name)) { parameters.add(name, value); } }
   * return parameters; } } catch (Exception exception) { throw new
   * RuntimeException(exception); } if (StringUtils.equalsIgnoreCase("get",
   * request.getMethod())) { HttpParameters parameters = new HttpParameters();
   * Enumeration paramEnum = request.getParameterNames(); while
   * (paramEnum.hasMoreElements()) { String name = (String)
   * paramEnum.nextElement(); String[] array = request.getParameterValues(name);
   * if (array != null && array.length > 0) { for (String value : array) {
   * parameters.add(name, value); } } } return parameters; } throw new
   * RuntimeException("unknown http method " + request.getMethod()); }
   */
  public static Long getNextServerSequence(Long value, int serverNumber) {
    if (value == null) {
      return new Long(serverNumber);
    }
    Long i = null;
    if (value > 99) {
      String string = value.toString();
      String substring = string.substring(0, string.length() - 2);
      i = new Long(substring + "00");
    } else {
      i = new Long(0);
    }
    long v = i + serverNumber;
    return v + 100;
  }

  public static int getServerNumber(BigInteger id) {
    String string = id.toString();
    String substring = string.substring(string.length() - 2, string.length());
    return Integer.parseInt(substring);
  }

  public static SortedSet<String> splitToSortedSet(String string) {
    if (StringUtils.isBlank(string)) {
      return null;
    }
    String[] array = splitPattern.split(string.trim(), 0);
    TreeSet<String> sortedSet = new TreeSet<String>();
    for (int x = 0; x < array.length; x++) {
      sortedSet.add(array[x]);
    }
    return sortedSet;
  }

  public static File getAppServerHome() {
    return new File(System.getProperty("catalina.home"));
  }

  public static File getHomeDirectory(String name, File defaultDirectory) throws Exception {
    String value = System.getenv(name);
    if (value != null)
      return new File(value);
    value = System.getProperty(name);
    if (value != null)
      return new File(value);
    Context context = (Context) new InitialContext().lookup("java:comp/env");
    try {
      String string = (String) context.lookup(name);
      if (StringUtils.isNotBlank(value))
        return new File(value);
    } catch (NameNotFoundException nameNotFoundException) {
    }
    defaultDirectory.mkdirs();
    return defaultDirectory;
  }

  public static Object getFirst(List list) {
    Iterator iterator = list.iterator();
    if (iterator.hasNext()) {
      return iterator.next();
    } else {
      return null;
    }
  }

  public static Map<String,String> toMapExcept(org.jdom.Element element, String exceptName) {
    Map<String,String> map = new HashMap<String,String>();
    for (Object object : element.getAttributes()) {
      org.jdom.Attribute attribute = (org.jdom.Attribute) object;
      String name = attribute.getName();
      if (!StringUtils.equals(name, exceptName)) {
        map.put(name, attribute.getValue());
      }
    }
    return map;
  }
}
